package topic

import (
	"context"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/service/backend/common/utils/pointy"
	"mall/service/forum/model"
	"mall/service/forum/rpc/internal/svc"
	"mall/service/forum/rpc/internal/utils/errorhandler"
	"mall/service/forum/rpc/types/forum"

	"github.com/zeromicro/go-zero/core/logx"
)

type BackendGetTopicListLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewBackendGetTopicListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BackendGetTopicListLogic {
	return &BackendGetTopicListLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *BackendGetTopicListLogic) BackendGetTopicList(in *forum.BackendTopicListReq) (*forum.BackendTopicListResp, error) {
	var orderBys []string
	if len(in.OrderBy) > 0 {
		for _, orderBy := range in.OrderBy {
			var order string = " ASC"
			if orderBy.Order == "1" {
				order = " DESC"
			}
			if orderBy.Field == "id" {
				orderBys = append(orderBys, model.ForumTopicColumns.TopicID+order)
			}
		}
	}

	var stati string
	var topicType string
	if len(in.FilterBy) > 0 {
		for _, filterBy := range in.FilterBy {
			switch filterBy.Field {
			case "status":
				stati = filterBy.In
			case "type":
				topicType = filterBy.In
			}
		}
	}

	var page uint64
	var pageSize uint64
	var restricted bool
	filterMap := make(map[string]map[string][]any)
	if in.Ids != nil {
		if vals := modelx.UniqueInterfaces(strings.TrimSpace(*in.Ids), 64); len(vals) > 0 {
			filterMap[model.ForumTopicColumns.TopicID] = map[string][]any{
				"in": vals,
			}
			restricted = true
		}
	}
	if !restricted {
		page = in.Page
		pageSize = in.PageSize
	}

	var statusIsSet bool
	switch topicType {
	case "1": // 问题（待审核）
		filterMap[model.ForumTopicColumns.ForumID] = map[string][]any{
			"moreThanOrEqualTo": {globalkey.ForumTopicTypeDefault},
		}
		filterMap[model.ForumTopicColumns.TopicStatus] = map[string][]any{
			"equalTo": {globalkey.StatusPending},
		}
		statusIsSet = true
	case "2": // 问题（已通过）
		filterMap[model.ForumTopicColumns.ForumID] = map[string][]any{
			"moreThanOrEqualTo": {globalkey.ForumTopicTypeDefault},
		}
		filterMap[model.ForumTopicColumns.TopicStatus] = map[string][]any{
			"equalTo": {globalkey.StatusApproved},
		}
		statusIsSet = true
	case "3": // 问题（已驳回）
		filterMap[model.ForumTopicColumns.ForumID] = map[string][]any{
			"moreThanOrEqualTo": {globalkey.ForumTopicTypeDefault},
		}
		filterMap[model.ForumTopicColumns.TopicStatus] = map[string][]any{
			"equalTo": {globalkey.StatusRejected},
		}
		statusIsSet = true
	case "4": // 问题（用户已删除）
		filterMap[model.ForumTopicColumns.ForumID] = map[string][]any{
			"moreThanOrEqualTo": {globalkey.ForumTopicTypeDefault},
		}
		filterMap[model.ForumTopicColumns.TopicStatus] = map[string][]any{
			"equalTo": {globalkey.StatusDeleted},
		}
		statusIsSet = true
	default:
		if in.ForumId != nil && *in.ForumId >= uint64(globalkey.ForumTopicTypeDefault) { // 指定版块的问题
			filterMap[model.ForumTopicColumns.ForumID] = map[string][]any{
				"equalTo": {*in.ForumId},
			}
		} else { // 默认为所有问题
			filterMap[model.ForumTopicColumns.ForumID] = map[string][]any{
				"moreThanOrEqualTo": []any{globalkey.ForumTopicTypeDefault},
			}
		}
	}

	if !statusIsSet {
		statusIn := modelx.UniqueInterfaces(stati, 32)
		statusMap := make(map[string][]any)
		if len(statusIn) > 0 {
			statusMap["in"] = statusIn
		}
		if in.Status != nil && *in.Status > 0 {
			statusMap["equalTo"] = []any{*in.Status - 1}
		}
		filterMap[model.ForumTopicColumns.TopicStatus] = statusMap
	}

	if in.AuthorId != nil {
		filterMap[model.ForumTopicColumns.TopicAuthorID] = map[string][]any{
			"equalTo": []any{*in.AuthorId},
		}
	}
	if in.Title != nil && len(strings.TrimSpace(*in.Title)) > 0 {
		filterMap[model.ForumTopicColumns.Title] = map[string][]any{
			"contain": []any{strings.TrimSpace(*in.Title)},
		}
	}
	if in.Slug != nil && len(strings.TrimSpace(*in.Slug)) > 0 {
		filterMap[model.ForumTopicColumns.Slug] = map[string][]any{
			"contain": []any{strings.TrimSpace(*in.Slug)},
		}
	}
	if in.Sticky != nil {
		filterMap[model.ForumTopicColumns.Sticky] = map[string][]any{
			"equalTo": []any{*in.Sticky},
		}
	}

	var orderBy string
	if len(orderBys) > 0 {
		orderBy = strings.Join(orderBys, ",")
	} else {
		orderBy = model.ForumTopicColumns.TopicModifiedDate + " DESC"
	}
	res, count, err := l.svcCtx.Topic.GetList(l.ctx, model.M{
		"page":     page,
		"pageSize": pageSize,
		"orderBy":  orderBy,
	}, 1, filterMap)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	resp := &forum.BackendTopicListResp{
		Total: uint64(count),
	}

	if len(res) > 0 {
		var tids []any
		for _, v := range res {
			tids = append(tids, v.TopicID)
		}

		mediaFilterMap := make(map[string]map[string][]any)
		mediaFilterMap[model.ForumAttachmentRelationshipColumns.ObjectID] = map[string][]any{
			"in": tids,
		}
		mediaFilterMap[model.ForumAttachmentRelationshipColumns.ObjectType] = map[string][]any{
			"equalTo": []any{globalkey.AttachmentLogTypeForumTopic},
		}
		var orderBy string = model.ForumAttachmentRelationshipColumns.AttachmentThumbnail + " DESC," +
			model.ForumAttachmentRelationshipColumns.AttachmentOrder
		mediaList, _, err := l.svcCtx.Topic.GetMedia(l.ctx, model.M{
			"pageSize": uint64(len(tids) * 3),
			"orderBy":  orderBy,
		}, -1, mediaFilterMap)
		if err != nil {
			return nil, err
		}

		idMediaMap := make(map[uint64][]*forum.ForumMediaInfo)
		if len(mediaList) > 0 {
			for _, v := range mediaList {
				idMediaMap[v.ObjectID] = append(idMediaMap[v.ObjectID], &forum.ForumMediaInfo{
					Uid:       pointy.GetPointer(v.AttachmentRelationshipID),
					Id:        pointy.GetPointer(strconv.FormatInt(int64(v.AttachmentID), 10)),
					Uri:       pointy.GetPointer(v.AttachmentSource),
					Url:       pointy.GetPointer(v.AttachmentImageURL),
					Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
					Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
					AltText:   pointy.GetPointer(v.AttachmentImageAlt),
					Meta:      pointy.GetPointer(v.AttachmentImageMeta),
				})
			}
		}

		metaFilterMap := make(map[string]map[string][]any)
		metaFilterMap[model.ForumTopicMetumColumns.TopicID] = map[string][]any{
			"in": tids,
		}
		metaList, _, err := l.svcCtx.Topic.GetMeta(l.ctx, model.M{}, -1, metaFilterMap)
		if err != nil {
			return nil, err
		}

		idMetaMap := make(map[uint64][]*forum.ForumMetaInfo)
		if len(metaList) > 0 {
			for _, v := range metaList {
				key := v.MetaKey
				if len(key) > 0 && v.MetaValue.Valid {
					idMetaMap[v.TopicID] = append(idMetaMap[v.TopicID], &forum.ForumMetaInfo{
						Id:    pointy.GetPointer(v.MetaID),
						Key:   pointy.GetPointer(key),
						Value: pointy.GetPointer(v.MetaValue.String),
					})
				}
			}
		}

		for _, v := range res {
			var media []*forum.ForumMediaInfo
			if ms, ok := idMediaMap[v.TopicID]; ok {
				media = ms[:]
			}
			var meta []*forum.ForumMetaInfo
			if ms, ok := idMetaMap[v.TopicID]; ok {
				meta = ms[:]
			}
			resp.List = append(resp.List, &forum.TopicInfo{
				Id:           pointy.GetPointer(v.TopicID),
				ForumId:      pointy.GetPointer(v.ForumID),
				AuthorId:     pointy.GetPointer(v.TopicAuthorID),
				Title:        pointy.GetPointer(v.Title),
				Slug:         pointy.GetPointer(v.Slug),
				Status:       pointy.GetPointer(uint32(v.TopicStatus)),
				Sticky:       pointy.GetPointer(uint32(v.Sticky)),
				PostCount:    pointy.GetPointer(uint32(v.PostCount)),
				CommentCount: pointy.GetPointer(uint32(v.CommentCount)),
				CreatedAt:    pointy.GetPointer(modelx.FormatTime(v.TopicDate, "")),
				UpdatedAt:    pointy.GetPointer(modelx.FormatTime(v.TopicModifiedDate, "")),
				Author: &forum.ForumAuthorInfo{
					Id:        pointy.GetPointer(v.TopicAuthorID),
					Name:      pointy.GetPointer(v.TopicAuthorNickname),
					Email:     pointy.GetPointer(v.TopicAuthorEmail),
					Avatar:    pointy.GetPointer(v.TopicAuthorAvatar),
					Ip:        pointy.GetPointer(v.TopicAuthorIP),
					Level:     pointy.GetPointer(uint32(v.TopicAuthorRankID)),
					Anonymous: pointy.GetPointer(uint32(v.TopicAuthorAnonymous)),
				},
				Media: media,
				Meta:  meta,
			})
		}
	}

	return resp, nil
}
